/*
 * burp.hpp
 *
 *  Created on: 2 May 2013
 *      Author: maneg
 */

#ifndef BURP_HPP_
#define BURP_HPP_
#ifdef WIN32
#include <windows.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
#include "tools.hpp"
#include <map>
#include <set>
#include <typeinfo>
#include <fstream>
#include <iostream>
namespace burp{

	struct platform_traits{
		const static std::string object_file_extention;
		const static std::string static_library_file_extention;
		const static std::string shared_library_file_extention;
		const static std::string executable_file_extention;
	};

#ifdef WIN32
		const std::string platform_traits::object_file_extention = std::string("obj");
		const std::string platform_traits::static_library_file_extention = std::string("lib");
		const std::string platform_traits::shared_library_file_extention = std::string("dll");
		const std::string platform_traits::executable_file_extention = std::string("exe");
#else
		const std::string platform_traits::object_file_extention = std::string("o");
		const std::string platform_traits::static_library_file_extention = std::string("a");
		const std::string platform_traits::shared_library_file_extention = std::string("so");
		const std::string platform_traits::executable_file_extention = std::string();
#endif



	class compiler_configuration{
	public:
		typedef std::vector<burp::directory> dirvec_t;
		typedef std::vector<std::string> ssvec_t;
	protected:
		typedef std::map<std::string,ssvec_t> argmap_t;
		bool debug_,shared_;
		int optimization_level_;
		int warning_level_;
		dirvec_t include_directories_;
		ssvec_t preprocessor_definitions_;
		argmap_t compiler_specific_arguments_;
		ssvec_t empty_;
		directory object_dir_;
		directory output_dir_;
	public:
		compiler_configuration() :
			debug_(false),
			shared_(false),
			optimization_level_(2),
			warning_level_(4),
			object_dir_(create_directory(path("./obj"))),
			output_dir_(path("."))
		{}
		explicit compiler_configuration(bool debug) :
				debug_(debug),
				shared_(false),
				optimization_level_(debug ? 0 : 2),
				warning_level_(4),
				object_dir_(create_directory(path("./obj"))),
				output_dir_(path("."))
		{}

		std::vector<burp::directory>& include_directories() {return include_directories_;}
		const std::vector<burp::directory>& include_directories() const {return include_directories_;}
		std::vector<std::string>& preprocessor_definitions(){return preprocessor_definitions_;}
		const std::vector<std::string>& preprocessor_definitions() const{return preprocessor_definitions_;}


		void add_include_directory(const burp::directory& dir){
			include_directories_.push_back(dir);
		}

		void add_preprocessor_definition(const std::string& str){
			preprocessor_definitions_.push_back(str);
		}

		void add_custom_argument(const std::string& arg, const std::string& comp){
			compiler_specific_arguments_[comp].push_back(arg);
		}

		const ssvec_t& custom_arguments(const std::string& comp) const {
			argmap_t::const_iterator it;
			if( (it = compiler_specific_arguments_.find(comp)) != compiler_specific_arguments_.end()){
				return it->second;
			}else{
				return empty_;
			}
		}

		bool is_debug() const {
			return debug_;
		}

		void debug(bool debug) {
			debug_ = debug;
		}

		int optimization_level() const {
			return optimization_level_;
		}

		void optimization_level(int optimizationLevel) {
			optimization_level_ = optimizationLevel;
		}

		int warning_level() const {
			return warning_level_;
		}


		void warning_level(int warningLevel) {
			warning_level_ = warningLevel < 0 ? 0 : (warningLevel > 4 ? 4 : warningLevel);
		}

			const directory& object_file_directory() const {
				return object_dir_;
			}

			void object_file_directory(const directory& objectDir) {
				object_dir_ = objectDir;
			}

			const directory& output_directory() const {
				return output_dir_;
			}

			void output_directory(const directory& outputDir) {
				output_dir_ = outputDir;
			}

	bool is_shared() const {
		return shared_;
	}

	void shared(bool shared) {
		shared_ = shared;
	}
};

	class linker_configuration{
		public:
			typedef std::vector<burp::directory> dirvec_t;
			typedef std::vector<burp::path> pathvec_t;
			typedef std::vector<std::string> ssvec_t;
		protected:
			typedef std::map<std::string,ssvec_t> argmap_t;
			bool debug_, shared_;
			dirvec_t library_include_directories_;
			argmap_t linker_specific_arguments_;
			pathvec_t link_libraries_;
			ssvec_t empty_;
			directory object_dir_;
			file output_file_;

		public:
			linker_configuration() :
				debug_(false),
				shared_(false),
				object_dir_(path(".")),
				output_file_(path("a.out"),false)
			{}
			explicit linker_configuration(bool debug) :
					debug_(debug),
					shared_(false),
					object_dir_(path(".")),
					output_file_(path("a.out"),false)
			{}

			std::vector<burp::directory>& library_include_directories() {return library_include_directories_;}
			const std::vector<burp::directory>& library_include_directories() const {return library_include_directories_;}
			std::vector<burp::path>& link_libraries() {return link_libraries_;}
			const std::vector<burp::path>& link_libraries() const{return link_libraries_;}

			void add_library_include_directory(const directory& dir){
				library_include_directories_.push_back(dir);
			}

			void add_link_library(const path& p){
				link_libraries_.push_back(p);
			}

			const directory& object_file_directory() const {
				return object_dir_;
			}

			void add_custom_argument(const std::string& arg, const std::string& comp){
				linker_specific_arguments_[comp].push_back(arg);
			}

			const ssvec_t& custom_arguments(const std::string& comp) const {
				argmap_t::const_iterator it;
				if( (it = linker_specific_arguments_.find(comp)) != linker_specific_arguments_.end()){
					return it->second;
				}else{
					return empty_;
				}
			}

			bool is_debug() const {
				return debug_;
			}

			void debug(bool debug) {
				debug_ = debug;
			}


			void object_file_directory(const directory& objectDir) {
				object_dir_ = objectDir;
			}

			const file& output_file() const {
				return output_file_;
			}

			void output_file(const file& outputFile) {
				output_file_ = outputFile;
			}

	bool is_shared() const {
		return shared_;
	}

	void shared(bool shared) {
		shared_ = shared;
	}
};


	class compiler{
		protected:
			burp::path compiler_command_;
			std::string name_;
			compiler(const std::string& cmd, const std::string& name) : compiler_command_(cmd),name_(name){}
			compiler() : compiler_command_(""){};
		public:
			typedef std::vector<std::string> argument_set_t;
			virtual ~compiler(){};
			virtual const std::string& compiler_name() const{return name_;}
			virtual void translate(argument_set_t& specific_arguments, const compiler_configuration& config) const = 0;
			virtual bool is_uptodate(const file& src, const file& obj) const = 0;
			virtual int compile(const burp::file& src_file, const file& obj_file,const argument_set_t& args, int verbosity_level, int worker_id=-1)const{
				std::vector<std::string> local_args = args;
				local_args.push_back("-o "+obj_file.native_path());
				local_args.push_back(src_file.native_path());

				if(verbosity_level > 0){
					if(verbosity_level == 1){
						std::cout << "[burp]";
						if(worker_id > 0) std::cout << "[" << worker_id << "] ";
						else std::cout << " ";
						std::cout << src_file.name() << std::endl;
					}else{
						std::cout << "[burp]";
						if(worker_id > 0) std::cout << "[" << worker_id << "] ";
						else std::cout << " ";
						std::cout << compiler_command_.native_path() << " ";
						for(argument_set_t::iterator it = local_args.begin();it!=local_args.end();++it){
							std::cout << *it << " ";
						}

						std::cout << std::endl;
					}
				}
				burp::process p(compiler_command_,local_args);
				return p.launch_and_wait();
			}
		};

	class dummy_compiler : public compiler{
	public:
		dummy_compiler(){name_ = "dummy";}
		virtual void translate(argument_set_t& specific_arguments, const compiler_configuration& config) const{};
		virtual bool is_uptodate(const file& src, const file& obj) const{return true;}
		virtual int compile(const burp::file& src_file, const file& obj_file,const argument_set_t& args, int verbosity_level, int worker_id=-1)const{return 0;}
	};


		class linker{
			protected:
				burp::path linker_command_;
				std::string linker_name_;
				linker(const std::string& cmd,const std::string& lname) : linker_command_(cmd),linker_name_(lname){}
				linker() : linker_command_(""){}
			public:
				typedef std::vector<std::string> argument_set_t;
				virtual void translate(argument_set_t& pre_specific_arguments, argument_set_t& post_specific_arguments,const linker_configuration& config) const = 0;
				virtual ~linker(){};
				virtual const std::string& linker_name() const{return linker_name_;}
				virtual int link(const file& output_file,const std::vector<burp::file>& files,const argument_set_t& pre_args, const argument_set_t& post_args, int verbosity, int worker_id=-1) const{
					std::vector<std::string> local_args  = pre_args;
					for(std::vector<burp::file>::const_iterator it = files.begin();it!=files.end();++it){
						local_args.push_back(it->native_path());
					}
					local_args.insert(local_args.end(),post_args.begin(),post_args.end());
					if(verbosity > 0){
						if(verbosity == 1){
							std::cout << "[burp]";
							if(worker_id > 0) std::cout << "[" << worker_id << "] ";
							else std::cout << " ";
							std::cout << "linking " << output_file.name() << std::endl;
 						}else{
							std::cout << "[burp]";
							if(worker_id > 0) std::cout << "[" << worker_id << "] ";
							else std::cout << " ";
							std::cout << linker_command_.native_path() << " ";
							for(argument_set_t::iterator it = local_args.begin();it!=local_args.end();++it){
								std::cout << *it << " ";
							}
							std::cout << std::endl;
						}
					}
					burp::process p(linker_command_,local_args);
					return p.launch_and_wait();
				}
			};

		class dummy_linker : public linker{
		public:
			dummy_linker() {linker_name_ = "dummy";}
			virtual void translate(argument_set_t& pre_specific_arguments, argument_set_t& post_specific_arguments,const linker_configuration& config) const{};
			virtual int link(const file& output_file,const std::vector<burp::file>& files,const argument_set_t& pre_args, const argument_set_t& post_args) const{return 0;}
		};

	typedef std::multimap<file, file> include_dependency_map_t;



	bool find_include(file& res, const file& current, const std::string& inc_file, const std::vector<directory>& include_dirs){
		directory d = current.parent();
		std::string check_for = d.native_path()+path::seperator()+inc_file;
		path p(check_for);
		if(p.exists()){
			res = file(p,false);
			return true;
		}else{
			for(std::vector<directory>::const_iterator it = include_dirs.begin(); it != include_dirs.end();++it){
				check_for = it->native_path() + path::seperator() + inc_file;
				path p(check_for);
				if(p.exists()){
					res = file(p,false);
					return true;
				}
			}
		}

		return false;
	}

	inline size_t next_char_is(const std::string& str, size_t idx, char c){
		for(size_t i = idx; i  < str.size();++i){
			char v = str[i];
			if(v==c) return i;
			else if( !(v==' ' || v == '\t')) return std::string::npos;
		}
		return std::string::npos;
	}


	bool parse_dfile(const file& dfile, time_t obj_time){
		std::ifstream str(dfile.native_path().c_str());
		if(str.is_open()){
			std::string line;
			bool first = true;
			while(std::getline(str,line)){
				if(first){
					first = false;
				}else{
					file f;
					if(*line.rbegin() == '\\'){
						f = file(path(line.substr(1,line.length()-3)));
					}else{
						f = file(path(line.substr(1)));
					}

					if(f.last_modified() >= obj_time) return false;
				}
			}
			return true;
		}else{
			return false;
		}
	}

	void generate_include_parse_line(const std::string& str){
		for(unsigned int i = 0; i < str.length();++i){

		}
	}

	void generate_include_graph(
			include_dependency_map_t& idm,
			const file& source_file,
			const std::vector<directory>& include_dirs)
	{
		std::ifstream str(source_file.native_path().c_str());
		if(str.is_open()){
			std::string line;
			while(std::getline(str,line)){
				size_t idx = line.find("#include");
				if(idx != std::string::npos){
					size_t idx2 = next_char_is(line,idx+8,'"');
					if(idx2 != std::string::npos){
						size_t idx3 = line.find('"',idx2+1);
						if(idx3 != std::string::npos){
							std::string inc = line.substr(idx2+1,idx3-idx2-1);
							file f;
							if(find_include(f,source_file,inc,include_dirs)){
								idm.insert(std::pair<file,file>(source_file,f));
								include_dependency_map_t::iterator it = idm.find(f);
								if(it == idm.end()){
									generate_include_graph(idm,f,include_dirs);
								}
							}else{
								throw std::runtime_error(std::string("missing include ")+inc);
							}
						}
					}
				}
			}
		}
	}

	class source_control{
	public:
		source_control() {};
	};

	class configurable{
	public:
		typedef std::pair<compiler_configuration,linker_configuration> config_pair_t;
	protected:
		std::pair<compiler_configuration,linker_configuration> debug_;
		std::pair<compiler_configuration,linker_configuration> release_;
		typedef std::map<std::string, std::pair<compiler_configuration,linker_configuration> >  custom_config_map_t;
		custom_config_map_t custom_;
		typedef std::map<std::string, sptr<compiler> > extention_compiler_map_t;
		sptr<linker> linker_;
		extention_compiler_map_t compilers_;
	public:


		configurable() : debug_(compiler_configuration(true),linker_configuration(true)){}

		config_pair_t& debug(){return debug_;}
		const config_pair_t& debug() const{return debug_;}
		config_pair_t& release(){return release_;}
		const config_pair_t& release() const{return release_;}
		void add_custom(const std::string& name, const config_pair_t& config){custom_[name] = config;}
		const config_pair_t& custom(const std::string& name) const {return custom_.find(name)->second;}
		config_pair_t& custom(const std::string& name) {return custom_.find(name)->second;}

		void add_compiler(const sptr<compiler>& comp, const std::vector<std::string>& extentions){
			for(std::vector<std::string>::const_iterator it = extentions.begin();it!=extentions.end();++it){
				compilers_[*it] = comp;
			}
		}


		void add_include_directory(const directory& dir){
			debug_.first.add_include_directory(dir);
			release_.first.add_include_directory(dir);
			for(custom_config_map_t::iterator it = custom_.begin();it!=custom_.end();++it){
				it->second.first.add_include_directory(dir);
			}
		}

		void add_preprocessor_definition(const std::string& str){
			debug_.first.add_preprocessor_definition(str);
			release_.first.add_preprocessor_definition(str);
			for(custom_config_map_t::iterator it = custom_.begin();it!=custom_.end();++it){
				it->second.first.add_preprocessor_definition(str);
			}
		}

		void add_custom_compiler_argument(const std::string& arg, const std::string& comp){
			debug_.first.add_custom_argument(arg,comp);
			release_.first.add_custom_argument(arg,comp);
			for(custom_config_map_t::iterator it = custom_.begin();it!=custom_.end();++it){
				it->second.first.add_custom_argument(arg,comp);
			}
		}


		void add_library_include_directory(const directory& dir){
			debug_.second.add_library_include_directory(dir);
			release_.second.add_library_include_directory(dir);
			for(custom_config_map_t::iterator it = custom_.begin();it!=custom_.end();++it){
				it->second.second.add_library_include_directory(dir);
			}
		}

		void add_link_library(const path& p){
			debug_.second.add_link_library(p);
			release_.second.add_link_library(p);
			for(custom_config_map_t::iterator it = custom_.begin();it!=custom_.end();++it){
				it->second.second.add_link_library(p);
			}
		}

		void add_custom_linker_argument(const std::string& arg, const std::string& comp){
			debug_.second.add_custom_argument(arg,comp);
			release_.second.add_custom_argument(arg,comp);
			for(custom_config_map_t::iterator it = custom_.begin();it!=custom_.end();++it){
				it->second.second.add_custom_argument(arg,comp);
			}
		}


		void add_compiler(const sptr<compiler>& comp, const std::string& extentions_comma_seperated){
			std::vector<std::string> vec;
			split(vec,extentions_comma_seperated,",");
			add_compiler(comp,vec);
		}
		const compiler& compiler_instance(const std::string& extention) const{
			extention_compiler_map_t::const_iterator it = compilers_.find(extention);
			if(it == compilers_.end()) throw std::invalid_argument("no handler for extension " + extention);
			return *it->second;
		}

		const linker& linker_instance() const{return *linker_;}
		void add_linker(const sptr<linker>& l){linker_ = l;}
	};

	class target : public configurable{
	public:
		typedef std::multimap<target*,target*> dependency_map_t;
	protected:
		std::vector<file> files_;
		std::string name_;
		token update_token_;
		std::pair<condition, mutex> done_;
	public:

		target(const std::string& name) : name_(name){
			debug().first.object_file_directory(create_directory(path(std::string("./obj/debug/")+name)));
			release().first.object_file_directory(create_directory(path(std::string("./obj/release/")+name)));
		}
		target(const std::string& name,const configurable& inherit) : configurable(inherit),name_(name){
			debug().first.object_file_directory(create_directory(path(debug().first.object_file_directory().native_path()+path::seperator()+name+path::seperator()+"debug")));
			release().first.object_file_directory(create_directory(path(release().first.object_file_directory().native_path()+path::seperator()+name+path::seperator()+"release")));
		}
		virtual ~target(){}

		const std::string& name() const {return name_;}
		void add(const file& f){files_.push_back(file(f.absolute_path()));}
		void add(const directory& dir){
			std::vector<path> pvec;
			for(std::vector<path>::const_iterator it = dir.list(pvec).begin();it!=pvec.end();++it){
				if(it->is_file()) files_.push_back(file(it->absolute_path(),false));
			}
		}
		void add_wildcard(const path& p){
			burp::match_wildcard(files_,p.native_path());
		}

		token& update_token(){return update_token_;}
		std::pair<condition,mutex>& done(){return done_;}


	const std::vector<file>& files() const {
		return files_;
	}
};



	class library : public target{
	protected:

	public:
		library(const std::string& name, bool shared) : target(name){
			debug().first.output_directory(create_directory(path("./lib/debug")));
			release().first.output_directory(create_directory(path("./lib/release")));
			if(shared){
				debug().first.shared(true);
				release().first.shared(true);
				debug().second.shared(true);
				release().second.shared(true);
				debug().second.output_file(file(path("./lib"+name+"d."+platform_traits::shared_library_file_extention),false));
				release().second.output_file(file(path("./lib"+name+"."+platform_traits::shared_library_file_extention),false));
			}
		}
		library(const std::string& name, const configurable& inherit) : target(name,inherit){
			debug().first.output_directory(create_directory(path("./lib/debug")));
			release().first.output_directory(create_directory(path("./lib/release")));
			debug().second.output_file(file(path("./lib"+name+"d."+platform_traits::shared_library_file_extention),false));
			release().second.output_file(file(path("./lib"+name+"."+platform_traits::shared_library_file_extention),false));
			debug().first.shared(true);
			release().first.shared(true);
			debug().second.shared(true);
			release().second.shared(true);
		}
		library(const std::string& name, const configurable& inherit, bool shared) : target(name,inherit){
			debug().first.output_directory(create_directory(path("./lib/debug")));
			release().first.output_directory(create_directory(path("./lib/release")));
			if(shared){
				debug().first.shared(true);
				release().first.shared(true);
				debug().second.shared(true);
				release().second.shared(true);
				debug().second.output_file(file(path("./lib/debug/lib"+name+"d."+platform_traits::shared_library_file_extention),false));
				release().second.output_file(file(path("./lib/release/lib"+name+"."+platform_traits::shared_library_file_extention),false));
			}
		}

	};

	class executable : public target{
	protected:
	public:
		executable(const std::string& name, bool shared) : target(name){
			debug().first.output_directory(create_directory(path("./bin/debug")));
			release().first.output_directory(create_directory(path("./bin/release")));
			debug().second.output_file(file(path("./bin/"+name+"d"+ (platform_traits::executable_file_extention.empty() ? std::string() : ("."+platform_traits::executable_file_extention))),false));
			release().second.output_file(file(path("./bin/"+name+ (platform_traits::executable_file_extention.empty() ? std::string() : ("."+platform_traits::executable_file_extention))),false));
		}

		executable(const std::string& name, const configurable& inherit) : target(name,inherit){
			debug().first.output_directory(create_directory(path("./bin/debug")));
			release().first.output_directory(create_directory(path("./bin/release")));
			debug().second.output_file(file(path("./bin/"+name+"d"+ (platform_traits::executable_file_extention.empty() ? std::string() : ("."+platform_traits::executable_file_extention))),false));
						release().second.output_file(file(path("./bin/"+name+ (platform_traits::executable_file_extention.empty() ? std::string() : ("."+platform_traits::executable_file_extention))),false));
		}
		executable(const std::string& name, const configurable& inherit, bool shared) : target(name,inherit){
			debug().first.output_directory(create_directory(path("./bin/debug")));
			release().first.output_directory(create_directory(path("./bin/release")));
			debug().second.output_file(file(path("./bin/"+name+"d"+ (platform_traits::executable_file_extention.empty() ? std::string() : ("."+platform_traits::executable_file_extention))),false));
						release().second.output_file(file(path("./bin/"+name+ (platform_traits::executable_file_extention.empty() ? std::string() : ("."+platform_traits::executable_file_extention))),false));
		}
	};


	class packaging : public target{
		protected:
		public:
	};



	class compile_task : public task{
	protected:
		const compiler& comp_;
		file file_;
		file obj_file_;
		const std::vector<std::string>& argset_ref_;
		sptr<scoped_condition_action> scon_;
		int verbosity_;
	public:
		compile_task(
				const compiler&  comp,
				const file& src_file,
				const file& obj_file,
				const std::vector<std::string>& argset,
				int verbosity,
				const sptr<scoped_condition_action> scon) : comp_(comp),file_(src_file),obj_file_(obj_file),argset_ref_(argset),scon_(scon),verbosity_(verbosity){}
		virtual bool operator()(int worker_id){
			comp_.compile(file_,obj_file_,argset_ref_,verbosity_,worker_id);
			return true;
		}
	};

	template <typename T>
	class build_target_task{
	protected:
		T& ref_;
		target& target_;
		std::string config_;
	public:
		build_target_task(target& target, T& proj, const std::string& config) : ref_(proj),target_(target),config_(config){}
		bool operator()(){
			if(target_.done().first.met()) return true;
			if(target_.update_token().grab()){
				ref_.build(target_,config_,false);
			}else{
				target_.done().first.wait(target_.done().second);
			}
			return true;
		}
	};


	class project : public configurable{
	protected:
		std::string name_;
		std::map<std::string, sptr<target> > targets_;
		sptr<executable> self_;
		sptr<thread_pool> pool_;
		typedef std::map<std::string, std::vector<sptr<target> > > depmap_t;
		depmap_t target_deps_;
		std::set<std::string> bulilt_targets_;
		command_line* cmd_;
		int verbosity_;
	public:
		project(const std::string& name, command_line& cmd) : name_(name), cmd_(&cmd){
			cmd.optional("help", "Print this help text");
			cmd.optional("jobs","number of concurrent jobs");
			cmd.optional("configuration","select configuration to build (debug|release|[custom])");
			cmd.optional("clean","cleans the target instead of building");
			cmd.optional("verbosity", "verbosity level (0=quiet,1=normal,2=verbose)");
			if(!cmd.check() || cmd.argument("help",false)){
				cmd.print();
				exit(0);
			}
			verbosity_ = cmd.argument<int>("verbosity",1);
			//this->add_compiler(sptr<compiler>(new dummy_compiler()),"h,hpp");
		}

		project(const std::string& name) : name_(name),cmd_(NULL),verbosity_(2){
		}


		void add_library(const sptr<library>& lib){
			targets_[lib->name()] = lib;
		}



		void add_self(const file& main_file){
			extention_compiler_map_t::const_iterator it = compilers_.find(main_file.extension());
			if(it == compilers_.end()) throw std::runtime_error("no compiler found for project " + name_);
			if(!linker_) throw std::runtime_error("no linker found for project " + name_);
			self_.reset(new executable("burp",true));
			self_->add_compiler(it->second,it->first);
			self_->add_linker(linker_);
			self_->add(main_file);
#ifdef WIN32
			self_->release().second.add_custom_argument("-subsystem,console","gcc");
#else
			self_->release().second.link_libraries().push_back(path("pthread"));
#endif
			self_->release().second.output_file(
					file(
							path(
									"./burp"+
									(platform_traits::executable_file_extention.empty() ?
											std::string() :
											("."+platform_traits::executable_file_extention))
								),false)
						);
		}

		void add_executable(const sptr<executable>& exec){
			targets_[exec->name()] = exec;
		}

		void add_dependecy(const std::string& a, const std::string& b){
			target_deps_[a].push_back(targets_[b]);
		}

		void add_dependecy(const sptr<target>& a, const sptr<target>& b){
			target_deps_[a->name()].push_back(b);
		}

		bool build(target& t, const std::string& configuration, bool clean=false){
			//first we should build the depdendencies;

			if(t.done().first.met()) return true;

			std::vector<sptr<burp::thread> > threads;

			std::vector<sptr<target> >::const_iterator it = target_deps_[t.name()].begin();
			for(; it != target_deps_[t.name()].end();++it){
				if(!clean && pool_ ){
					threads.push_back(sptr<thread>(new thread(sptr<build_target_task<project> >(new build_target_task<project>(**it,*this,configuration)))));
				}else{
					build(**it,configuration,clean);
				}
			}

			if(!clean && pool_){
				for(std::vector<sptr<burp::thread> >::iterator it = threads.begin();it!=threads.end();++it){
					(*it)->join();
				}
			}


			const target::config_pair_t& cfg =
					configuration == "debug" ? t.debug() : (configuration == "release" ? t.release() : t.custom(configuration));


			directory obj_dir = cfg.first.object_file_directory();


			std::map<std::string,compiler::argument_set_t> argset;

			std::vector<file> linker_files;
			bool all_up_to_date = true;

			sptr<condition> cond;
			sptr<mutex> cond_mtx;
			sptr<scoped_condition_action> action;
			if(pool_){
				cond_mtx.reset(new mutex());
				cond.reset(new condition());
				action.reset(new scoped_condition_action(cond,cond_mtx));
			}


			for(std::vector<file>::const_iterator it = t.files().begin();it!=t.files().end();++it){
				std::string fn = it->name();
				std::string ext =  it->extension();

				file obj_file(path(cfg.first.object_file_directory().native_path() + path::seperator() + filename_change_extention(*it,"."+platform_traits::object_file_extention)),false);

				if(clean){
					obj_file.remove();
				}else{
					try{

						const compiler& comp =  t.compiler_instance(ext);

						std::map<std::string,compiler::argument_set_t>::iterator aset = argset.find(ext);

						if(aset == argset.end()){
							comp.translate(argset[ext],cfg.first);
						}


						bool uptodate = comp.is_uptodate(*it,obj_file);

						if(!uptodate){
							if(pool_){
								pool_->push_back_task(sptr<task>(new compile_task(comp,*it,obj_file,argset[ext],verbosity_,action)));
							}else{
								comp.compile(*it,obj_file,argset[ext],verbosity_);
							}
						}
						all_up_to_date &= uptodate;
						linker_files.push_back(file(obj_file.absolute_path(),!pool_));
					}catch(const std::exception&){
						//std::cout << "[what] " << ex.what() << std::endl;
					}
				}

			}



			if(pool_){
				action.reset(NULL);
				cond->wait(*cond_mtx);
			}


			file output_file = cfg.second.output_file();
			if(clean){
				output_file.remove();
			}else{
				if(!all_up_to_date || !output_file.exists()){
					if(&t == self_.get()){
#ifdef WIN32
							std::cout << "[burp] moving existing executable..." << std::endl;
							HMODULE hModule = GetModuleHandleA(NULL);
							CHAR ep[MAX_PATH];
							GetModuleFileNameA(hModule, ep, MAX_PATH);
							std::string path_str(ep);
							std::string::size_type pos = path_str.find(".exe");
							std::string new_path = path_str.substr(0,pos)+"_old.exe";
							MoveFileExA(path_str.c_str(),new_path.c_str(),MOVEFILE_REPLACE_EXISTING);
#endif
					}
					linker::argument_set_t largset_pre,largset_post;
					const linker& link = t.linker_instance();
					link.translate(largset_pre,largset_post,cfg.second);
					link.link(output_file,linker_files,largset_pre,largset_post,verbosity_);
				}
			}

			t.done().first.broadcast(t.done().second);
			return !all_up_to_date || !output_file.exists();
		}

		void clean(target& t, const std::string& configuration){
			build(t,configuration,true);
		}

		void build_all(const std::string& configuration){
			bulilt_targets_.clear();
			std::map<std::string, sptr<target> >::iterator it = targets_.begin();
			std::vector<sptr<burp::thread> > threads;

			for(;it!=targets_.end();++it){
				if(pool_ ){
					threads.push_back(sptr<thread>(new thread(sptr<build_target_task<project> >(new build_target_task<project>(*(it->second),*this,configuration)))));
				}else{
					build(*(it->second),configuration);
				}
			}

			if(pool_){
				for(std::vector<sptr<burp::thread> >::iterator it = threads.begin();it!=threads.end();++it){
					(*it)->join();
				}
			}


		}

		void clean_all(const std::string& configuration){
			std::map<std::string, sptr<target> >::iterator it = targets_.begin();
			for(;it!=targets_.end();++it){
				clean(*(it->second),configuration);
			}
		}


		int operator() (){
			if(!cmd_){
				throw std::runtime_error("project automatic dispatch function requires command line instance");
			}
			int jobs = cmd_->argument<int>("jobs",1);
			if(jobs > 1) {
				if(verbosity_ > 0) std::cout << "[burp] using " << jobs << " jobs" << std::endl;
				pool_.reset(new thread_pool(jobs));
			}

			std::vector<std::string>::const_iterator it = cmd_->targets().begin();
			for(;it!=cmd_->targets().end();++it){
				std::string config = cmd_->argument<std::string>("configuration","release");
				bool clean = cmd_->argument<bool>("clean",false);
				if(*it == "all"){
					if(clean){
						clean_all(config);
					}else{
						if(self_){
							if(build(*self_,"release")){
								std::cout << "[burp] restarting..." << std::endl;
								std::cout.flush();
								burp::process::restart_current(cmd_->argc(), cmd_->argv());
							}
						}
						build_all(config);
					}
				}else if(*it == "self"){
					if(self_) build(*self_,"release");
				}else{
					if(self_){
						if(build(*self_,"release")){
							std::cout << "[burp] restarting..." << std::endl;
							std::cout.flush();
							burp::process::restart_current(cmd_->argc(), cmd_->argv());
						}
					}
					build(*targets_[*it],config,clean);
				}
			}

			return 0;
		}

	};


}

#endif /* BURP_HPP_ */
